#!/usr/bin/env python
#!encoding: utf-8

import os, sys, subprocess
import urllib2
import time
import json
import getopt
import re
import yaml
from utils import switch, validate_ip, validate_port, validate_delay_time, get_version

pyadmin_cmd_list = ['reload', 'shutdown', 'info', 'upgrade']
pyctl_cmd_list = ['start', 'stop', 'restart', 'status', 'kill', 'mkcd', 'rmcd', 'showcd']
## 单子命令列表(无参)
single_cmd_list = ['reload', 'shutdown', 'info', 'showcd', 'status']
default_map = {
    'config_file'   : '/etc/pyadmin.yml', 
    'py_ip'    : '127.0.0.1',
    'py_port'  : '5000',
    'delay_time'    : 3
}
cfg_map = {}
cmdline_map = {}

def validate_yml(cfg_path):
    try:
        cfg = yaml.load(file(cfg_path))
    except Exception, e:
        print(e)
        return False
        
    if not cfg.has_key('host'):
        print('You must specify a root node named "host"')
        return False
    if not cfg['host'].has_key('pyctl'):
        print('Can\'t find the "pyctl" block in config file')
        return False
    cfg4pyctl = cfg['host']['pyctl']
    if cfg4pyctl.has_key('py_ip') and not validate_ip(cfg4pyctl['py_ip']):
        print('Invalid IP address')
        return False
    if cfg4pyctl.has_key('py_port') and not validate_port(cfg4pyctl['py_port']):
        print('Invalid port number')
        return False
    if cfg4pyctl.has_key('delay_time') and not validate_delay_time(cfg4pyctl['delay_time']):
        print('Invalid IP address')
        return False

def validate_opts(opts):
    for key, val in opts:
        ## help, version的选项优先级最高
        if key in ['--help']:
            usage()
            sys.exit()
        if key in ['-v', '--version']:
            print(get_version())
            sys.exit()
        if key in ['-c', '--config']:
            if os.path.isfile(val):
                cmdline_map['config_file'] =  val
            else:
                print('Invalid config file path')
                sys.exit()
        if key in ['-h', '--host']:
            if validate_ip(val):
                cfg_map['py_ip'] = val
                if not validate_yml(val):
                    print('Error while parsing the config file')
            else:
                print('Invalid IP address')
                sys.exit()
        if key in ['-p', '--port']:
            if validate_port(val):
                cmdline_map['py_port'] = int(val)
            else:
                print('Invalid port number')
                sys.exit()
        if key in ['-d', '--delay']:
            if validate_delay_time(val):
                cmdline_map['delay_time'] = val
            else:
                print('Invalid delay time')
                sys.exit()

    cfg_map['config_file'] = cmdline_map['config_file'] if cmdline_map.has_key('config_file') else default_map['config_file']
    cfg4pyctl = yaml.load(file(cfg_map['config_file']))['host']['pyctl']

    if cmdline_map.has_key('py_ip'):
        cfg_map['py_ip'] = cmdline_map['py_ip']
    elif cfg4pyctl.has_key('py_ip'):
        cfg_map['py_ip'] = cfg4pyctl['py_ip']
    else:
        cfg_map['py_ip'] = default_map['py_ip']

    if cmdline_map.has_key('py_port'):
        cfg_map['py_port'] = cmdline_map['py_port']
    elif cfg4pyctl.has_key('py_port'):
        cfg_map['py_port'] = cfg4pyctl['py_port']
    else:
        cfg_map['py_port'] = default_map['py_port']

    if cmdline_map.has_key('delay_time'):
        cfg_map['delay_time'] = cmdline_map['delay_time']
    elif cfg4pyctl.has_key('delay_time'):
        cfg_map['delay_time'] = cfg4pyctl['delay_time']
    else:
        cfg_map['delay_time'] = default_map['delay_time']  

def validate_cmds(args):
    '''
    验证pyctl指定的子命令
    '''
    if(len(args) == 0):
        print('Empty args')
        return False
    if not args[0] in pyadmin_cmd_list and not args[0] in pyctl_cmd_list:
        print('Invalid Command')
        return False
    if not args[0] in single_cmd_list and len(args) < 2:
        print('Sorry, you must specify at least 2 args: command and process\'s name')
        return False
    return True

def usage():
    pass

def show_status(resultStr):
    ## json.loads将得到字典对象
    resultObj = json.loads(resultStr)

    
    for i in resultObj.keys():
        j = resultObj[i]
        print('%(name)-20s pid: %(pid)-18s status: %(status)-4s desc: %(desc)s' % 
            {'name': j['name'], 'pid':j['pid'], 'status': j['status'], 'desc': j['msg']}
        )

def show_result(operrationMsg):
    resultObj = {}
    resultObj = json.loads(operrationMsg)
    for i in resultObj.keys():
        j = resultObj[i]
        print('%(name)-20s message: %(msg)-50s' % {'name': j['name'], 'msg': j['msg']})

def dispatch_cmds(args):
    pyaddr = 'http://%s:%s' % (cfg_map['py_ip'], cfg_map['py_port'])
    proc_url = '%s/process/%s?namelist=%s' % (pyaddr, args[0], ','.join(args[1:]))
    pyadmin_url = '%s/pyadmin/%s' % (pyaddr, args[0])
    status_url = '%s/process/%s?namelist=%s' % (pyaddr, 'status', ','.join(args[1:]))

    for case in switch(args[0]):
        #####################################################################################
        ## pyadmin内置命令
        if case('reload'):
            print(urllib2.urlopen(pyadmin_url).read())
            sys.exit()
        if case('shutdown'):
            ## 如果pyadmin顺利终止, 则无法返回响应, 由pyctl打印success. 
            ## 反之, 则可以打印出它的响应为failed
            try:
                urllib2.urlopen(status_url).read()
            except:
                print('Error: PYAdmin may not be running...')
                sys.exit()
            try:
                msg = urllib2.urlopen(pyadmin_url).read()
                if not msg == 'failed':
                    print('success')
                else:
                    print(msg)
            except Exception, e:
                print('success')
            sys.exit()
        if case('upgrade'):
            print('upgrading...')
            upgrade_url = '%s/pyadmin/%s?pkgaddr=%s' % (pyaddr, args[0], args[1:])
            msg = urllib2.urlopen(upgrade_url).read()
            print(msg)

        #####################################################################################
        ## pyadmin进程管理命令
        if case('restart'):
            stop_url = '%s/process/%s?namelist=%s' % (pyaddr, 'stop', ','.join(args[1:]))
            start_url = '%s/process/%s?namelist=%s' % (pyaddr, 'start', ','.join(args[1:]))
            print('stopping...')
            msg = urllib2.urlopen(stop_url).read()
            show_result(msg)
            time.sleep(cfg_map['delay_time'])
            print('starting...')
            msg = urllib2.urlopen(start_url).read()
            show_result(msg)
            time.sleep(cfg_map['delay_time'])
            sys.exit()
        if case('showcd'):
            print(urllib2.urlopen(proc_url).read())
            sys.exit()
        if case('status'):
            if len(args) == 1:
                proc_url = proc_url + 'all'
            resultStr = urllib2.urlopen(proc_url).read()
            show_status(resultStr)
            sys.exit()
        if case('start'):
            print('starting...')
            break
        if case('stop'):
            print('stopping...')
            break
        if case('kill'):
            print('killing...')
            break
        if case('mkcd'):
            print('cooldown...')
            break
        if case('rmcd'):
            print('meltup...')
            break
        ## 默认值
        if case():
            print('Invalid command...')
            sys.exit()
    msg = urllib2.urlopen(proc_url).read()
    show_result(msg)
    time.sleep(cfg_map['delay_time'])

    resultStr = urllib2.urlopen(status_url).read()
    show_status(resultStr)

def main():
    ## 解析命令行选项
    try:
        opts, args = getopt.getopt(
            sys.argv[1:],
            'vc:h:p:d:',
            ['help', 'version', 'config=','host=','port=', 'delay=']
        )
    except getopt.GetoptError:
        sys.exit()

    ## 验证命令行选项
    validate_opts(opts)

    ## 验证子命令, 并发送请求
    if validate_cmds(args):
        dispatch_cmds(args)
    else:
        exit(1)

if __name__ == '__main__':
    main()